/*
 * @Author: Wangjun
 * @Date: 2021-05-21 15:01:45
 * @LastEditTime: 2025-05-22 15:21:06
 * @LastEditors: wangjun haodreams@163.com
 * @Description:时间常用库
 * @FilePath: \libs\easy\time.go
 * hnxr
 */
package easy

import (
	"bytes"
	"database/sql/driver"
	"errors"
	"strconv"
	"strings"
	"time"
)

const (
	YYYY = "2006"   //年份
	MM   = "01"     //月份
	YM   = "200601" //年月
	HHmm = "15:04"  //时分
	Hm   = "1504"   //时分

	YYYYMMDDHHMMSSMill = "2006-01-02 15:04:05.000" //年月日 时分秒,毫秒
	YMDHMSMill         = "20060102150405.000"
	YYYYMMDDHHMMSS     = "2006-01-02 15:04:05" //年月日 时分秒
	YYYYMMDDHHMM       = "2006-01-02 15:04"    //年月日 时分
	YYYYMMDD           = "2006-01-02"          //年月日
	YMD                = "20060102"            //精简版 年月日

	//js 习惯
	YYYYMMDDHHmmssSSS = "2006-01-02 15:04:05.000" //年月日 时分秒,毫秒
	YYYYMMDDHHmmss    = "2006-01-02 15:04:05"     //年月日 时分秒
	YMDHmsSSS         = "20060102150405.000"      //年月日 时分秒,毫秒 .000：表示毫秒（精确到 3 位，不足补零）
	YYMDHmsSSS        = "060102150405.000"        //年月日 时分秒,毫秒 .000：表示毫秒（精确到 3 位，不足补零）
	YYYYMMDDHHmm      = "2006-01-02 15:04"        //年月日 时分
	YMDHms            = "20060102150405"          //精简版 年月日时分秒
	YMDHm             = "200601021504"            //精简版 年月日时分
	HHmmssSSS         = "15:04:05.000"            //时分秒,毫秒
	HmsSSS            = "150405.000"            //时分秒,毫秒
	HHmmss            = "15:04:05"                //时分秒
	Hms               = "150405"                  //时分秒
)

const (
	absoluteZeroYear = -292277022399

	secondsPerMinute = 60
	secondsPerHour   = 60 * secondsPerMinute
	secondsPerDay    = 24 * secondsPerHour
	secondsPerWeek   = 7 * secondsPerDay
	daysPer400Years  = 365*400 + 97
	daysPer100Years  = 365*100 + 24
	daysPer4Years    = 365*4 + 1

	internalYear             = 1
	unixToInternal     int64 = (1969*365 + 1969/4 - 1969/100 + 1969/400) * secondsPerDay
	absoluteToInternal int64 = (absoluteZeroYear - internalYear) * 365.2425 * secondsPerDay
	internalToAbsolute       = -absoluteToInternal
)

/**
 * @description: 获取当前字符串时间
 * @param {*}
 * @return {*}
 */
func Now(fmt ...string) string {
	if len(fmt) == 0 {
		return time.Now().Format(YYYYMMDDHHMMSS)
	}
	return time.Now().Format(fmt[0])
}

/**
 * @description: 当前时间按指定格式显示
 * @param {...string} fmt
 * @return {*}
 */
func FormatNow(fmt ...string) string {
	if len(fmt) == 0 {
		return Now()
	}
	return time.Now().Format(fmt[0])
}

/**
 * @description: 获取当前时间带毫秒支持
 * @param {*}
 * @return {*}
 */
func NowWithMill() string {
	return time.Now().Format(YYYYMMDDHHMMSSMill)
}

// 获取指定时间的0点秒数
func Today(now, offset int64) int64 {
	now = now + offset //加上8小时，取余，不要跑到昨天
	now -= now % 86400 //8点钟
	now -= offset      //减去时区(8小时)误差
	return now
}

/**
 * @description: 获取修正后的0点秒数
 * @param {} now //当前时间
 * @param {int64} offset //时区偏移的秒数
 * @return {*} //返回修正后的0点
 */
func FixZoneZeroClock(now, offset int64) int64 {
	now = now + offset //加上8小时，取余，不要跑到昨天
	now -= now % 86400 //8点钟
	now -= offset      //减去时区(8小时)误差
	return now
}

// 格式化一堆时间
func FormatTimes(ts []int64, fmt ...string) (sts []string) {
	sts = make([]string, len(ts))
	for i, t := range ts {
		sts[i] = FormatTime(t, fmt...)
	}
	return
}

// FormatTime 时间格式化
// // Deprecated: this function simply calls Format.
func FormatTime(t int64, fmt ...string) string {
	if len(fmt) > 0 {
		return time.Unix(t, 0).Format(fmt[0])
	}
	return time.Unix(t, 0).Format(YYYYMMDDHHmmss)
}

// FormatUTCTime 时间格式化
// Deprecated: this function simply calls FormatUTC.
func FormatUTCTime(t int64, fmt ...string) string {
	return FormatUTC(t, fmt...)
}

// FormatTime 时间格式化
// func Format(t int64, fmt ...string) string {
// 	if len(fmt) > 0 {
// 		return time.Unix(t, 0).Format(fmt[0])
// 	}
// 	return time.Unix(t, 0).Format(YYYYMMDDHHMMSS)
// }

// FormatUTCTime 时间格式化
func FormatUTC(t int64, fmt ...string) string {
	if len(fmt) > 0 {
		return time.Unix(t, 0).UTC().Format(fmt[0])
	}
	return time.Unix(t, 0).UTC().Format(YYYYMMDDHHMMSS)
}

// 解析时分秒对应的秒数
func ParserHHMMSS(stime string) (t int64, err error) {
	ss := strings.Split(stime, ":")
	if len(ss) != 3 {
		return
	}
	h, err := strconv.Atoi(ss[0])
	if err != nil {
		return
	}
	h *= 3600
	m, err := strconv.Atoi(ss[1])
	if err != nil {
		return
	}
	m *= 60
	s, err := strconv.Atoi(ss[2])
	if err != nil {
		return
	}
	t = int64(h + m + s)
	return
}

// ParserTime64 字符串转时间
func ParserTime64(stime string, fmt ...string) (t int64, err error) {
	var tm time.Time
	if len(fmt) > 0 {
		tm, err = time.Parse(fmt[0], stime)
	} else {
		tm, err = time.Parse(YYYYMMDDHHMMSS, stime)
	}
	if err != nil {
		return
	}
	t = tm.Unix()
	if t < 0 { //没有日期的情况，解析出来是负数
		h, m, s := tm.Clock()
		t = int64(h*3600 + m*60 + s)
	}
	return
}

// ParserLocalTime64 字符串转时间
func ParserLocalTime64s(stimes []string, fmt ...string) (ts []int64, err error) {
	ts = make([]int64, len(stimes))
	for i, st := range stimes {
		t, err := ParserLocalTime64(st, fmt...)
		if err != nil {
			return nil, err
		}
		ts[i] = t
	}
	return
}

// ParserLocalTime64 字符串转时间
func ParserLocalTime64(stime string, fmt ...string) (t int64, err error) {
	var tm time.Time
	if len(fmt) > 0 {
		tm, err = time.ParseInLocation(fmt[0], stime, time.Local)
	} else {
		tm, err = time.ParseInLocation(YYYYMMDDHHMMSS, stime, time.Local)
	}
	if err != nil {
		return
	}
	t = tm.Unix()
	if t < 0 { //没有日期的情况，解析出来是负数
		h, m, s := tm.Clock()
		t = int64(h*3600 + m*60 + s)
		// _, offset := tm.Zone()
		// t -= int64(offset)
	}
	return
}

// ConvertFormat 高性能转换函数 YYYY-MM-DD 转换为20060102的go语言模式
func ConvertFormat(fmt string) string {
	l := len(fmt)
	newFmt := make([]byte, l)
	for i := 0; i < l; i++ {
		switch fmt[i] {
		case 'Y': //年
			if (i + 3) < l {
				if fmt[i:i+4] == "YYYY" {
					newFmt[i] = '2'
					newFmt[i+1] = '0'
					newFmt[i+2] = '0'
					newFmt[i+3] = '6'
					i += 3
					continue
				} else if fmt[i:i+2] == "YY" {
					newFmt[i] = '0'
					newFmt[i+1] = '6'
					i++
					continue
				}
			} else if (i + 1) < l {
				if fmt[i:i+2] == "YY" {
					newFmt[i] = '0'
					newFmt[i+1] = '6'
					i++
					continue
				}
			}
		case 'M': //月
			if (i + 1) < l {
				if fmt[i:i+2] == "MM" {
					newFmt[i] = '0'
					newFmt[i+1] = '1'
					i++
					continue
				}
			}
		case 'D': //日
			if (i + 1) < l {
				if fmt[i:i+2] == "DD" {
					newFmt[i] = '0'
					newFmt[i+1] = '2'
					i++
					continue
				}
			}
		case 'H': //时
			if (i + 1) < l {
				if fmt[i:i+2] == "HH" {
					newFmt[i] = '1'
					newFmt[i+1] = '5'
					i++
					continue
				}
			}
		case 'm': //分
			if (i + 1) < l {
				if fmt[i:i+2] == "mm" {
					newFmt[i] = '0'
					newFmt[i+1] = '4'
					i++
					continue
				}
			}
		case 's': //秒
			if (i + 1) < l {
				if fmt[i:i+2] == "ss" {
					newFmt[i] = '0'
					newFmt[i+1] = '5'
					i++
					continue
				}
			}
		case 'S': //毫秒
			if (i + 1) < l {
				if fmt[i:i+3] == "SSS" {
					newFmt[i] = '0'
					newFmt[i+1] = '0'
					newFmt[i+2] = '0'
					i += 2
					continue
				}
			}
		}
		newFmt[i] = fmt[i]
	}
	return string(newFmt)
}

type Time int64
type Date int64

func (m Time) Int64() int64 {
	return int64(m)
}

func (m Time) Value() (v driver.Value, err error) {
	v = int64(m)
	return
}

func (m *Time) Set(t int64) {
	*m = Time(t)
}

func (m Time) String() string {
	return time.Unix(int64(m), 0).Format(YYYYMMDDHHMMSS)
}

// gorm 接口
func (m *Time) Scan(v interface{}) (err error) {
	switch vv := v.(type) {
	case time.Time:
		*m = Time(vv.Unix())
	case *time.Time:
		if vv != nil {
			*m = Time(vv.Unix())
		}
	case int:
		*m = Time(vv)
	case int32:
		*m = Time(vv)
	case int64:
		*m = Time(vv)
	case uint:
		*m = Time(vv)
	case uint32:
		*m = Time(vv)
	case uint64:
		*m = Time(vv)
	default:
		err = errors.New("unknown type")
	}
	return
}

func (t Time) MarshalJSON() ([]byte, error) {
	return []byte(time.Unix(int64(t), 0).Format("\"2006-01-02 15:04:05\"")), nil
}

func (t *Time) UnmarshalJSON(b []byte) error {
	b = bytes.Trim(b, "\"")
	val := string(b)
	if val == "" {
		return nil
	}
	tm, err := time.ParseInLocation(YYYYMMDDHHMMSS, val, time.Local)
	if err != nil {
		return err
	}
	*t = Time(tm.Unix())
	return nil
}

func (m Date) Int64() int64 {
	return int64(m)
}

func (m Date) Value() (v driver.Value, err error) {
	v = int64(m)
	return
}

func (m *Date) Scan(v interface{}) (err error) {
	switch vv := v.(type) {
	case time.Time:
		*m = Date(vv.Unix())
	case *time.Time:
		if vv != nil {
			*m = Date(vv.Unix())
		}
	case int:
		*m = Date(vv)
	case int32:
		*m = Date(vv)
	case int64:
		*m = Date(vv)
	case uint:
		*m = Date(vv)
	case uint32:
		*m = Date(vv)
	case uint64:
		*m = Date(vv)
	default:
		err = errors.New("unknown type")
	}
	return
}

func (t Date) MarshalJSON() ([]byte, error) {
	return []byte(time.Unix(int64(t), 0).Format("\"2006-01-02\"")), nil
}

func (t *Date) UnmarshalJSON(b []byte) error {
	b = bytes.Trim(b, "\"")
	val := string(b)
	if val == "" {
		return nil
	}
	tm, err := time.ParseInLocation(YYYYMMDD, val, time.Local)
	if err != nil {
		return err
	}
	*t = Date(tm.Unix())
	return nil
}

func (m *Date) Set(t int64) {
	*m = Date(t)
}

func (m Date) String() string {
	return time.Unix(int64(m), 0).Format(YYYYMMDD)
}

// AbsTime is like clock but operates on an absolute time.
func AbsTime(abs int64) (hour, min, sec int) {
	sec = int(abs % secondsPerDay)
	hour = sec / secondsPerHour
	sec -= hour * secondsPerHour
	min = sec / secondsPerMinute
	sec -= min * secondsPerMinute
	return
}

func isLeap(year int) bool {
	return year%4 == 0 && (year%100 != 0 || year%400 == 0)
}

// AbsDate is like date but operates on an absolute time.
func AbsDate(sec int64) (year int, month int, day int, yday int) {
	abs := uint64(int64(sec) + (unixToInternal + internalToAbsolute))

	// Split into time and day.
	d := abs / secondsPerDay

	// Account for 400 year cycles.
	n := d / daysPer400Years
	y := 400 * n
	d -= daysPer400Years * n

	// Cut off 100-year cycles.
	// The last cycle has one extra leap year, so on the last day
	// of that year, day / daysPer100Years will be 4 instead of 3.
	// Cut it back down to 3 by subtracting n>>2.
	n = d / daysPer100Years
	n -= n >> 2
	y += 100 * n
	d -= daysPer100Years * n

	// Cut off 4-year cycles.
	// The last cycle has a missing leap year, which does not
	// affect the computation.
	n = d / daysPer4Years
	y += 4 * n
	d -= daysPer4Years * n

	// Cut off years within a 4-year cycle.
	// The last year is a leap year, so on the last day of that year,
	// day / 365 will be 4 instead of 3. Cut it back down to 3
	// by subtracting n>>2.
	n = d / 365
	n -= n >> 2
	y += n
	d -= 365 * n

	year = int(int64(y) + absoluteZeroYear)
	yday = int(d)

	day = yday
	if isLeap(year) {
		// Leap year
		switch {
		case day > 31+29-1:
			// After leap day; pretend it wasn't there.
			day--
		case day == 31+29-1:
			// Leap day.
			month = 2
			day = 29
			return
		}
	}

	// Estimate month on assumption that every month has 31 days.
	// The estimate may be too low by at most one month, so adjust.
	month = day / 31
	end := int(daysBefore[month+1])
	var begin int
	if day >= end {
		month++
		begin = end
	} else {
		begin = int(daysBefore[month])
	}

	month++ // because January is 1
	day = day - begin + 1
	return
}

// daysBefore[m] counts the number of days in a non-leap year
// before month m begins. There is an entry for m=12, counting
// the number of days before January of next year (365).
var daysBefore = [...]int32{
	0,
	31,
	31 + 28,
	31 + 28 + 31,
	31 + 28 + 31 + 30,
	31 + 28 + 31 + 30 + 31,
	31 + 28 + 31 + 30 + 31 + 30,
	31 + 28 + 31 + 30 + 31 + 30 + 31,
	31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
	31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
	31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
	31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
	31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
}
